package com.wuliwu.autoapi.service;

import com.wuliwu.autoapi.annotation.Desc;
import com.wuliwu.autoapi.annotation.Return;
import com.wuliwu.autoapi.bean.Api;
import com.wuliwu.autoapi.bean.ApiFunction;
import com.wuliwu.autoapi.bean.ApiParameter;
import com.wuliwu.autoapi.bean.ReturnBean;
import com.wuliwu.autoapi.util.PackageUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ApiService {

    private static final String controllerPackageName = "com.wuliwu.autoapi.controller";

    private static final String pojoPackageName = "com.wuliwu.autoapi.bean";

    private static Logger LOG = LoggerFactory.getLogger(ApiService.class);

    private List<Map<String, String>> functions = null;

    private Map<String, ApiFunction> functionMap = null;

    /**
     * 查找所有接口
     */
    public List<Map<String, String>> getFunctions() {

        if (functions != null) {
            return functions;
        }

        functions = new ArrayList<>();
        functionMap = new HashMap<>();

        //扫描所有controller
        List<Class<Object>> packageClass = PackageUtil.findPackageClass(controllerPackageName);

        for (Class<Object> clazz : packageClass) {

            //取得Controller的地址
            String classMappingValue = getClassMappingValue(clazz);

            Method[] methods = clazz.getMethods();
            for (Method method : methods) {

                //取得方法的地址
                String[] methodMappingAndType = this.getMethodMappingValue(method);
                if (methodMappingAndType == null) {
                    continue;
                }

                String methodMappingValue = methodMappingAndType[0];
                String methodRequestType = methodMappingAndType[1];

                //拼接方法请求地址
                String url = classMappingValue;
                if (methodMappingValue.length() > 0) {
                    url += ((url.length() > 0 && methodMappingValue.charAt(0) != '/') ? "/" : "") + methodMappingValue;
                }

                //方法的参数
                List<ApiParameter> apiParameters = new ArrayList<>();
                for (Parameter parameter : method.getParameters()) {

                    //取参数的类名
                    String className = parameter.getType().getName(); //如 java.lang.String

                    //过滤掉HttpServlet
                    if (className.startsWith("javax.servlet.http")) {
                        continue;
                    }

                    //如果参数是pojo 取pojo所有成员
                    if (className.startsWith(pojoPackageName)) {
                        addPojoParameters(className, apiParameters);
                    } else {
                        createApiParameter(className, apiParameters, parameter.getName(), getDesc(parameter), getRequired(parameter));
                    }
                }

                //方法描述
                String desc = getDesc(method);

                //存入列表 方法简要信息
                Map<String, String> funcInfo = new HashMap<>();
                funcInfo.put("url", url);
                funcInfo.put("desc", desc);
                functions.add(funcInfo);

                //存入map 方法详情
                ApiFunction apiFunction = new ApiFunction();
                apiFunction.setUrl(url);
                apiFunction.setDesc(desc);
                apiFunction.setRequestType(methodRequestType);
                apiFunction.setParameters(apiParameters);
                apiFunction.setReturnBeans(getReturnBeans(method));
                functionMap.put(url, apiFunction);
            }
        }

        functions.forEach(item -> {
            LOG.debug("描述:" + item.get("desc"));
            LOG.debug("地址:" + item.get("url"));
        });

        return functions;
    }

    /**
     * 根据接口地址取得接口详情
     */
    public ApiFunction getFunction(String url) {
        if (functionMap == null) {
            getFunctions();
        }
        return functionMap.get(url);
    }

    /**
     * 查找接口列表树
     */
    public List<Api> listApiTree() {

        List<Api> apis = new ArrayList<>();

        //扫描所有controller
        List<Class<Object>> packageClass = PackageUtil.findPackageClass(controllerPackageName);

        for (Class<Object> clazz : packageClass) {

            //取得Controller的地址
            String classMappingValue = getClassMappingValue(clazz);
            if (StringUtils.isEmpty(classMappingValue)){
                continue;
            }

            Api controllerApi = new Api();
            controllerApi.setName(classMappingValue);

            Method[] methods = clazz.getMethods();
            for (Method method : methods) {

                //取得方法的地址
                String methodMappingValue = this.getMethodMapping(method);
                if (methodMappingValue == null) {
                    continue;
                }

                Api methodApi = new Api();
                methodApi.setName(methodMappingValue);

                //作为子节点放入controller
                if (controllerApi.getChildren() == null){
                    controllerApi.setChildren(new ArrayList<>());
                }
                controllerApi.getChildren().add(methodApi);
            }

            if (!CollectionUtils.isEmpty(controllerApi.getChildren())){
                apis.add(controllerApi);
            }
        }

        return apis;
    }

    private String getClassMappingValue(Class<Object> clazz) {

        RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class);
        if (classRequestMapping == null) {
            return "";
        }

        String value = classRequestMapping.value()[0];
        if (value.length() > 0 && value.indexOf(0) == '/') {
            value = value.substring(1);
        }

        return value;
    }

    private String[] getMethodMappingValue(Method method) {

        String value = null;
        String requestType = null;

        GetMapping methodGetMapping = method.getAnnotation(GetMapping.class);
        if (methodGetMapping != null) {
            value = methodGetMapping.value()[0];
            requestType = "get";
        } else {
            PostMapping methodPostMapping = method.getAnnotation(PostMapping.class);
            if (methodPostMapping != null) {
                value = methodPostMapping.value()[0];
                requestType = "post";
            }
        }

        if (StringUtils.isEmpty(value)) {
            return null;
        }

        if (value.indexOf(0) == '/') {
            value = value.substring(1);
        }

        return new String[]{value, requestType};
    }

    private String getMethodMapping(Method method) {

        String value = null;

        GetMapping methodGetMapping = method.getAnnotation(GetMapping.class);
        if (methodGetMapping != null) {
            value = methodGetMapping.value()[0];
        } else {
            PostMapping methodPostMapping = method.getAnnotation(PostMapping.class);
            if (methodPostMapping != null) {
                value = methodPostMapping.value()[0];
            }  else {
                RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
                if (methodRequestMapping != null) {
                    value = methodRequestMapping.value()[0];
                }
            }
        }

        if (StringUtils.isEmpty(value)) {
            return null;
        }

        if (value.indexOf(0) == '/') {
            value = value.substring(1);
        }

        return value;
    }

    private String getDesc(Method method) {

        Desc[] descAnnotations = method.getAnnotationsByType(Desc.class);
        return getDesc(descAnnotations);
    }

    private String getDesc(Parameter parameter) {

        Desc[] descAnnotations = parameter.getAnnotationsByType(Desc.class);
        return getDesc(descAnnotations);
    }

    private String getDesc(Field field) {

        Desc[] descAnnotations = field.getAnnotationsByType(Desc.class);
        return getDesc(descAnnotations);
    }

    private String getDesc(Desc[] descAnnotations) {
        if (descAnnotations != null && descAnnotations.length > 0) {
            return descAnnotations[0].value();
        }

        return "";
    }

    private boolean getRequired(Parameter parameter) {
        RequestParam[] paramAnnotations = parameter.getAnnotationsByType(RequestParam.class);
        return paramAnnotations != null && paramAnnotations.length != 0 && paramAnnotations[0].required();
    }

    private boolean getRequired(Field field) {
        return field.getAnnotation(NotBlank.class) != null || field.getAnnotation(NotEmpty.class) != null
                || field.getAnnotation(NotNull.class) != null;
    }

    private static String getParameterTypeName(String fullName) {

        //取简化名称
        String[] arr = fullName.split("\\.");
        String name = arr[arr.length - 1].replace(";", "");

        //如果是数组 如 [Ljava.lang.Integer;
        if (fullName.startsWith("[L")) {
            name += "[]";
        }

        return name;
    }

    private void addPojoParameters(String className, List<ApiParameter> apiParameters) {
        try {
            Class c = Class.forName(className);
            Field[] fs = c.getDeclaredFields();
            for (Field field : fs) {

                //如果有忽略注解 则跳过
                // || field.getAnnotation(ColumnIgnore.class) != null
                if (field.getAnnotation(Desc.class) == null ) {
                    continue;
                }

                createApiParameter(field.getType().getName(), apiParameters, field.getName(), getDesc(field), getRequired(field));
            }
        } catch (ClassNotFoundException e) {
            LOG.error("取pojo的参数失败");
        }
    }

    private void createApiParameter(String className, List<ApiParameter> apiParameters, String name, String desc, boolean required) {
        ApiParameter apiParameter = new ApiParameter();
        apiParameter.setName(name); //参数名
        apiParameter.setDesc(desc); //参数描述
        apiParameter.setType(getParameterTypeName(className)); //参数类型
        apiParameter.setRequired(required); //是否必填
        apiParameters.add(apiParameter);
    }

    private List<ReturnBean> getReturnBeans(Method method) {
        Return[] descAnnotations = method.getAnnotationsByType(Return.class);
        if (descAnnotations == null || descAnnotations.length == 0) {
            return null;
        }

        List<ReturnBean> returnBeans = new ArrayList<>();
        Class[] classes = descAnnotations[0].value();
        for (Class aClass : classes) {
            returnBeans.add(getReturnBean(aClass));
        }

        return returnBeans;
    }

    private ReturnBean getReturnBean(Class c) {
        List<ApiParameter> beanFields = new ArrayList<>();
        Field[] fs = c.getDeclaredFields();
        for (Field field : fs) {
            createApiParameter(c.getName(), beanFields, field.getName(), getDesc(field), getRequired(field));
        }
        return new ReturnBean(c.getName().substring(c.getName().lastIndexOf('.') + 1), beanFields);
    }

    public static void main(String[] args) {

    }
}
